package com.koron.util.redisconfig;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.koron.css2.serviceManage.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class RedisConfig {

    private static Logger log = LoggerFactory.getLogger(RedisConfig.class);

    @Resource
    private RedisProperties redisPro;

    @Bean
    public JedisPoolConfig redisLinkPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(redisPro.getMaxTotal());
        config.setMaxIdle(redisPro.getMaxIdle());
        config.setMinIdle(redisPro.getMinIdle());
        config.setMaxWaitMillis(redisPro.getMaxWaitMillis());
        config.setTestOnBorrow(redisPro.isTestOnBorrow());
        config.setTestOnReturn(redisPro.isTestOnReturn());
        config.setTestWhileIdle(redisPro.isTestWhileIdle());
        config.setNumTestsPerEvictionRun(redisPro.getNumTestsPerEvictionRun());
        config.setTimeBetweenEvictionRunsMillis(redisPro.getTimeBetweenEvictionRunsMillis());
        log.info("redis配置信息为：" + JsonUtils.objectToJson(config));
        return config;
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig config) {
        JedisConnectionFactory factory = null;
        if (StringUtils.isNotBlank(redisPro.getIp())) {
            log.info("单点redis配置为空");
            factory = new JedisConnectionFactory(config);
            factory.setHostName(redisPro.getIp());
            factory.setPort(redisPro.getPort());
        } else if (StringUtils.isNotBlank(redisPro.getClusterNodes())) {
            log.info("初始化redis集群配置信息,读取的配置信息为：" + JsonUtils.objectToJson(config));
            String[] cNodes = redisPro.getClusterNodes().split(",");
            Set<RedisNode> jedisClusterNode = new HashSet<>();
            // 分割出集群节点
            String[] hp;
            for (String node : cNodes) {
                hp = node.split(":");
                jedisClusterNode.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
            }
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
            clusterConfiguration.setClusterNodes(jedisClusterNode);
            factory = new JedisConnectionFactory(clusterConfiguration, config);
        } else {
            log.error("redis配置错误，请检查redis配置信息是否正确");
            throw new RuntimeException("redis配置错误，请检查redis配置信息是否正确");
        }
        factory.setPassword(StringUtils.isBlank(redisPro.getPassword())?null:redisPro.getPassword());
        return factory;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<String, String>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public RedissonClient redisson() {
        if (StringUtils.isNotBlank(redisPro.getIp())) {
            Config config = new Config();
            SingleServerConfig ssConfig = config.useSingleServer().setAddress("redis://" + redisPro.getIp() + ":" + redisPro.getPort());
            if(StringUtils.isNotBlank(redisPro.getPassword())) {
                ssConfig.setPassword(redisPro.getPassword());
            }
            ssConfig.setConnectionMinimumIdleSize(redisPro.getMinIdle());
            return Redisson.create(config);
        }else if (StringUtils.isNotBlank(redisPro.getClusterNodes())) {
            Config config = new Config();
            String[] cNodes = redisPro.getClusterNodes().split(",");
            for (int i = 0; i < cNodes.length; i++) {
                cNodes[i] = "redis://" + cNodes[i];
            }
            ClusterServersConfig csConfig= config.useClusterServers().addNodeAddress(cNodes);
            if(StringUtils.isNotBlank(redisPro.getPassword())) {
                csConfig.setPassword(redisPro.getPassword());
            }
            return Redisson.create(config);
        }else {
            log.info("无redis配置");
            return null;
        }
    }

}
